package top.healthylife.docx4jhelper.utils;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.google.common.base.Charsets;
import io.swagger.annotations.ApiModelProperty;
import top.healthylife.docx4jhelper.annotation.Docx4jTblField;
import top.healthylife.docx4jhelper.enums.Docx4jNodeType;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.Docx4J;
import org.docx4j.Docx4jProperties;
import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.convert.out.ConversionHTMLStyleElementHandler;
import org.docx4j.convert.out.HTMLSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.model.properties.table.tr.TrHeight;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.contenttype.ContentType;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.WordprocessingML.*;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.Color;
import org.docx4j.wml.*;
import org.docx4j.wml.P.Hyperlink;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.PBdr;
import org.docx4j.wml.PPrBase.Spacing;
import org.docx4j.wml.SectPr.PgBorders;
import org.docx4j.wml.SectPr.PgMar;
import org.docx4j.wml.SectPr.PgSz;
import org.docx4j.wml.SectPr.Type;
import org.docx4j.wml.TcPrInner.GridSpan;
import org.docx4j.wml.TcPrInner.HMerge;
import org.docx4j.wml.TcPrInner.VMerge;
import org.w3c.dom.Element;
import top.healthylife.docx4jhelper.annotation.Docx4jTblField;
import top.healthylife.docx4jhelper.enums.Docx4jNodeType;

import javax.imageio.ImageIO;
import javax.xml.bind.JAXBElement;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.List;
import java.util.*;

@Slf4j
@SuppressWarnings("all")
public class Docx4jUtil {
    public static ObjectFactory factory = Context.getWmlObjectFactory();

    /**
     * 新增超链接
     */
    public static void createHyperlink(WordprocessingMLPackage wordMLPackage, MainDocumentPart mainPart,
                                       ObjectFactory factory, P paragraph, String url, String value, String cnFontName, String enFontName,
                                       String fontSize) throws Exception {
        if (StringUtils.isBlank(enFontName)) {
            enFontName = "Times New Roman";
        }
        if (StringUtils.isBlank(cnFontName)) {
            cnFontName = "微软雅黑";
        }
        if (StringUtils.isBlank(fontSize)) {
            fontSize = "22";
        }
        org.docx4j.relationships.ObjectFactory reFactory = new org.docx4j.relationships.ObjectFactory();
        org.docx4j.relationships.Relationship rel = reFactory.createRelationship();
        rel.setType(Namespaces.HYPERLINK);
        rel.setTarget(url);
        rel.setTargetMode("External");
        mainPart.getRelationshipsPart().addRelationship(rel);
        StringBuffer sb = new StringBuffer();
        // addRelationship sets the rel's @Id
        sb.append("<w:hyperlink r:id=\"");
        sb.append(rel.getId());
        sb.append("\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" ");
        sb.append("xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" >");
        sb.append("<w:r><w:rPr><w:rStyle w:val=\"Hyperlink\" />");
        sb.append("<w:rFonts  w:ascii=\"");
        sb.append(enFontName);
        sb.append("\"  w:hAnsi=\"");
        sb.append(enFontName);
        sb.append("\"  w:eastAsia=\"");
        sb.append(cnFontName);
        sb.append("\" w:hint=\"eastAsia\"/>");
        sb.append("<w:sz w:val=\"");
        sb.append(fontSize);
        sb.append("\"/><w:szCs w:val=\"");
        sb.append(fontSize);
        sb.append("\"/></w:rPr><w:t>");
        sb.append(value);
        sb.append("</w:t></w:r></w:hyperlink>");

        Hyperlink link = (Hyperlink) XmlUtils.unmarshalString(sb.toString());
        paragraph.getContent().add(link);
    }

    public static String getElementContent(Object obj) throws Exception {
        StringWriter stringWriter = new StringWriter();
        TextUtils.extractText(obj, stringWriter);
        return stringWriter.toString();
    }

    /**
     * 得到指定类型的元素
     */
    public static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
        List<Object> result = new ArrayList<Object>();
        if (obj instanceof JAXBElement)
            obj = ((JAXBElement<?>) obj).getValue();
        if (obj.getClass().equals(toSearch))
            result.add(obj);
        else if (obj instanceof ContentAccessor) {
            List<?> children = ((ContentAccessor) obj).getContent();
            for (Object child : children) {
                result.addAll(getAllElementFromObject(child, toSearch));
            }
        }
        return result;
    }

    /**
     * 保存WordprocessingMLPackage
     */
    public static void saveWordPackage(WordprocessingMLPackage wordPackage, File file) throws Exception {
        wordPackage.save(file);
    }

    /**
     * 新建WordprocessingMLPackage
     */
    public static WordprocessingMLPackage createWordprocessingMLPackage() throws Exception {
        return WordprocessingMLPackage.createPackage();
    }

    /**
     * 加载带密码WordprocessingMLPackage
     */
    public static WordprocessingMLPackage loadWordprocessingMLPackageWithPwd(String filePath, String password)
            throws Exception {
        OpcPackage opcPackage = WordprocessingMLPackage.load(new java.io.File(filePath), password);
        WordprocessingMLPackage wordMLPackage = (WordprocessingMLPackage) opcPackage;
        return wordMLPackage;
    }

    /**
     * 加载WordprocessingMLPackage
     */
    public static WordprocessingMLPackage loadWordprocessingMLPackage(String filePath) throws Exception {
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(filePath));
        return wordMLPackage;
    }

    /*------------------------------------Word 表格相关---------------------------------------------------  */

    /**
     * 跨列合并
     */
    public static void mergeCellsHorizontalByGridSpan(Tbl tbl, int row, int fromCell, int toCell) {
        if (row < 0 || fromCell < 0 || toCell < 0) {
            return;
        }
        List<Tr> trList = getTblAllTr(tbl);
        if (row > trList.size()) {
            return;
        }
        Tr tr = trList.get(row);
        List<Tc> tcList = getTrAllCell(tr);
        for (int cellIndex = Math.min(tcList.size() - 1, toCell); cellIndex >= fromCell; cellIndex--) {
            Tc tc = tcList.get(cellIndex);
            TcPr tcPr = getTcPr(tc);
            if (cellIndex == fromCell) {
                GridSpan gridSpan = tcPr.getGridSpan();
                if (gridSpan == null) {
                    gridSpan = new GridSpan();
                    tcPr.setGridSpan(gridSpan);
                }
                gridSpan.setVal(BigInteger.valueOf(Math.min(tcList.size() - 1, toCell) - fromCell + 1));
            } else {
                tr.getContent().remove(cellIndex);
            }
        }
    }

    /**
     * 跨列合并
     */
    public static void mergeCellsHorizontal(Tbl tbl, int row, int fromCell, int toCell) {
        if (row < 0 || fromCell < 0 || toCell < 0) {
            return;
        }
        List<Tr> trList = getTblAllTr(tbl);
        if (row > trList.size()) {
            return;
        }
        Tr tr = trList.get(row);
        List<Tc> tcList = getTrAllCell(tr);
        for (int cellIndex = fromCell, len = Math.min(tcList.size() - 1, toCell); cellIndex <= len; cellIndex++) {
            Tc tc = tcList.get(cellIndex);
            TcPr tcPr = getTcPr(tc);
            HMerge hMerge = tcPr.getHMerge();
            if (hMerge == null) {
                hMerge = new HMerge();
                tcPr.setHMerge(hMerge);
            }
            if (cellIndex == fromCell) {
                hMerge.setVal("restart");
            } else {
                hMerge.setVal("continue");
            }
        }
    }

    /**
     * 跨行合并
     */
    public static void mergeCellsVertically(Tbl tbl, int col, int fromRow, int toRow) {
        if (col < 0 || fromRow < 0 || toRow < 0) {
            return;
        }
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            Tc tc = getTc(tbl, rowIndex, col);
            if (tc == null) {
                break;
            }
            TcPr tcPr = getTcPr(tc);
            VMerge vMerge = tcPr.getVMerge();
            if (vMerge == null) {
                vMerge = new VMerge();
                tcPr.setVMerge(vMerge);
            }
            if (rowIndex == fromRow) {
                vMerge.setVal("restart");
            } else {
                vMerge.setVal("continue");
            }
        }
    }

    /**
     * 得到指定位置的单元格
     */
    public static Tc getTc(Tbl tbl, int row, int cell) {
        if (row < 0 || cell < 0) {
            return null;
        }
        List<Tr> trList = getTblAllTr(tbl);
        if (row >= trList.size()) {
            return null;
        }
        List<Tc> tcList = getTrAllCell(trList.get(row));
        if (cell >= tcList.size()) {
            return null;
        }
        return tcList.get(cell);
    }

    /**
     * 得到所有表格
     */
    public static List<Tbl> getAllTbl(WordprocessingMLPackage wordMLPackage) {
        MainDocumentPart mainDocPart = wordMLPackage.getMainDocumentPart();
        List<Object> objList = getAllElementFromObject(mainDocPart, Tbl.class);
        if (objList == null) {
            return null;
        }
        List<Tbl> tblList = new ArrayList<Tbl>();
        for (Object obj : objList) {
            if (obj instanceof Tbl) {
                Tbl tbl = (Tbl) obj;
                tblList.add(tbl);
            }
        }
        return tblList;
    }

    /**
     * 删除指定位置的表格,删除后表格数量减一
     */
    public static boolean removeTableByIndex(WordprocessingMLPackage wordMLPackage, int index) throws Exception {
        boolean flag = false;
        if (index < 0) {
            return flag;
        }
        List<Object> objList = wordMLPackage.getMainDocumentPart().getContent();
        if (objList == null) {
            return flag;
        }
        int k = -1;
        for (int i = 0, len = objList.size(); i < len; i++) {
            Object obj = XmlUtils.unwrap(objList.get(i));
            if (obj instanceof Tbl) {
                k++;
                if (k == index) {
                    wordMLPackage.getMainDocumentPart().getContent().remove(i);
                    flag = true;
                    break;
                }
            }
        }
        return flag;
    }

    /**
     * 获取单元格内容, 无分割符
     */
    public static String getTblContentStr(Tbl tbl) throws Exception {
        return getElementContent(tbl);
    }

    /**
     * 获取表格内容
     */
    public static List<String> getTblContentList(Tbl tbl) throws Exception {
        List<String> resultList = new ArrayList<String>();
        List<Tr> trList = getTblAllTr(tbl);
        for (Tr tr : trList) {
            StringBuffer sb = new StringBuffer();
            List<Tc> tcList = getTrAllCell(tr);
            for (Tc tc : tcList) {
                sb.append(getElementContent(tc) + ",");
            }
            resultList.add(sb.toString());
        }
        return resultList;
    }

    public static TblPr getTblPr(Tbl tbl) {
        TblPr tblPr = tbl.getTblPr();
        if (tblPr == null) {
            tblPr = new TblPr();
            tbl.setTblPr(tblPr);
        }
        return tblPr;
    }

    /**
     * 设置表格总宽度
     */
    public static void setTableWidth(Tbl tbl, String width) {
        if (StringUtils.isNotBlank(width)) {
            TblPr tblPr = getTblPr(tbl);
            TblWidth tblW = tblPr.getTblW();
            if (tblW == null) {
                tblW = new TblWidth();
                tblPr.setTblW(tblW);
            }
            tblW.setW(new BigInteger(width));
            tblW.setType("dxa");
            //tblW.setType("pct");
        }
    }

    /**
     * 获取表格宽度   ???
     *
     * @param w
     * @return
     */
    public static TblWidth getTblWidth(int w) {

        TblWidth width = new TblWidth();

        width.setW(BigInteger.valueOf(w));

        width.setType("pct");

        //此处试了几种方式均不好用,只有这个pct的合适,50分之一是一个百分点,5000为百分之百的宽度
        return width;

    }

    /**
     * 创建表格(默认水平居中,垂直居中)
     */
    public static Tbl createTable(WordprocessingMLPackage wordPackage, int rowNum, int colsNum) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        int widthTwips = getWritableWidth(wordPackage);
        int colWidth = widthTwips / colsNum;
        Tbl tbl = factory.createTbl();
        //tblPr
        TblPr tblPr = new TblPr();
        //style
        CTTblPrBase.TblStyle tblStyle = new CTTblPrBase.TblStyle();
        tblStyle.setVal("3");
        tblPr.setTblStyle(tblStyle);
        //tblInd
        TblWidth tblWidth = new TblWidth();
        tblWidth.setW(BigInteger.valueOf(0));
        tblWidth.setType("dxa");
        tblPr.setTblInd(tblWidth);
        //layout 固定列宽
        CTTblLayoutType tbll = factory.createCTTblLayoutType();
        tbll.setType(STTblLayoutType.FIXED);
        tblPr.setTblLayout(tbll);
        //width
        TblWidth tblWidth1 = new TblWidth();
        tblWidth1.setType("dxa");
        tblWidth1.setW(BigInteger.valueOf(widthTwips));
        tblPr.setTblW(tblWidth1);
        //tblGrid
        TblGrid tblGrid = factory.createTblGrid();
        List<TblGridCol> gridCol = tblGrid.getGridCol();
        if (gridCol == null) {
            TblGridCol[] objects = ArrayUtil.newArray(TblGridCol.class, colsNum);
            gridCol = Arrays.asList(objects);
        }
        gridCol.forEach(e -> e.setW(BigInteger.valueOf(colWidth)));

        tbl.setTblPr(tblPr);
        tbl.setTblGrid(tblGrid);
        for (int i = 0; i < rowNum; i++) {
            Tr dataTr = factory.createTr();
            for (int j = 0; j < colsNum; j++) {
                Tc tc = factory.createTc();
                TcPr tcPr = factory.createTcPr();
                tcPr.setTcFitText(null);

                setTcWidth(tc, colWidth + "");
                dataTr.getContent().add(tc);
            }
            tbl.getContent().add(dataTr);
        }
        log.info("创建表格,{}行,{}列,总宽度:{},列宽:{}", rowNum, colsNum, widthTwips, colWidth);
        return tbl;
    }


    /**
     * 创建表格并初始化数据
     *
     * @param wordPackage
     * @param data        数据
     * @return tbl
     * @throws Exception
     */
    public static Tbl createTableAndInitDataByMap(WordprocessingMLPackage wordPackage, List<LinkedHashMap<String, Object>> data) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        Set<String> effectiveFieldStr = data.get(0).keySet();
        List<String> effectiveStr = new ArrayList<>(effectiveFieldStr);
        int colsNum = effectiveFieldStr.size();
        int rowNum = data.size();
        //可用宽度
        int widthAvaliable = getWritableWidth(wordPackage);
        //列宽
        int colWidth = widthAvaliable / colsNum;
        Tbl tbl = factory.createTbl();
        //tblPr
        TblPr tblPr = new TblPr();
        //style
        CTTblPrBase.TblStyle tblStyle = new CTTblPrBase.TblStyle();
        tblStyle.setVal("10");
        tblPr.setTblStyle(tblStyle);
        //tblInd
        TblWidth tblWidth = new TblWidth();
        tblWidth.setW(BigInteger.valueOf(0));
        tblWidth.setType("dxa");
        tblPr.setTblInd(tblWidth);
        //layout 固定列宽
        CTTblLayoutType tbll = factory.createCTTblLayoutType();
        tbll.setType(STTblLayoutType.FIXED);
        tblPr.setTblLayout(tbll);
        //width
        TblWidth tblWidth1 = new TblWidth();
        tblWidth1.setType("dxa");
        tblWidth1.setW(BigInteger.valueOf(widthAvaliable));
        tblPr.setTblW(tblWidth1);
        //tblGrid
        TblGrid tblGrid = factory.createTblGrid();
        List<TblGridCol> gridCol = tblGrid.getGridCol();
        if (gridCol == null) {
            TblGridCol[] objects = ArrayUtil.newArray(TblGridCol.class, colsNum);
            gridCol = Arrays.asList(objects);
        }
        gridCol.forEach(e -> e.setW(BigInteger.valueOf(colWidth)));
        tbl.setTblPr(tblPr);
        tbl.setTblGrid(tblGrid);
        //border
        addBorders(tbl);
        BooleanDefaultTrue booleanDefaultTrue = new BooleanDefaultTrue();
        booleanDefaultTrue.setVal(Boolean.TRUE);
        //margin
        setTableCellMargin(tbl, "100", "" + colWidth / 10, "100", "" + colWidth / 10);
        for (int i = 0; i < rowNum + 1; i++) {
            Tr dataTr = factory.createTr();
            for (int j = 0; j < colsNum; j++) {
                Tc tc = factory.createTc();
                //设置单元格样式
                TcPr tcPr = factory.createTcPr();

                tcPr.setTcFitText(booleanDefaultTrue);
                //setTcWidth(tc, colWidth + "");
                dataTr.getContent().add(tc);
                //渲染数据
                P p = factory.createP();
                RPr rPr = null;
                //插入内容
                String content;
                //首行渲染标题
                if (i == 0) {
                    content = effectiveStr.get(j);
                    //首行标题加粗
                    rPr = factory.createRPr();
                    rPr.setB(booleanDefaultTrue);
                    Docx4jUtil.setFontFamily(rPr, "黑体", "Times New Roman");
                } else {
                    //渲染数据
                    LinkedHashMap<String, Object> t = data.get(i - 1);

                    String key = effectiveStr.get(j);
                    content = ObjectUtil.toString(t.get(key));
                    content = WordFileUtil.doBreakLine(WordFileUtil.getContentWidth(content), colWidth, content, new StringBuilder());

                }
                //插入数据
                Text t = factory.createText();

                t.setValue(content);
                org.docx4j.wml.R run = factory.createR();
                run.setRPr(rPr);
                run.getContent().add(t);
                p.getContent().add(run);
                tc.getContent().add(p);
            }
            tbl.getContent().add(dataTr);
        }
        log.info("创建表格,{}行,{}列,总宽度:{},列宽:{}", rowNum, colsNum, widthAvaliable, colWidth);
        return tbl;
    }

    /**
     * 创建表格并初始化数据
     *
     * @param wordPackage
     * @param data        数据
     * @return tbl
     * @throws Exception
     */
    public static <T> Tbl createTableAndInitData(WordprocessingMLPackage wordPackage, List<T> data) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        List<Field> effectiveField = WordFileUtil.getEffectiveField(data.get(0));
        int colsNum = effectiveField.size();
        int rowNum = data.size();
        //可用宽度
        int widthAvaliable = getWritableWidth(wordPackage);
        //列宽
        int colWidth = widthAvaliable / colsNum;
        Tbl tbl = factory.createTbl();
        //tblPr
        TblPr tblPr = new TblPr();
        //style
        CTTblPrBase.TblStyle tblStyle = new CTTblPrBase.TblStyle();
        tblStyle.setVal("10");
        tblPr.setTblStyle(tblStyle);
        //tblInd
        TblWidth tblWidth = new TblWidth();
        tblWidth.setW(BigInteger.valueOf(0));
        tblWidth.setType("dxa");
        tblPr.setTblInd(tblWidth);
        //layout 固定列宽
        CTTblLayoutType tbll = factory.createCTTblLayoutType();
        tbll.setType(STTblLayoutType.FIXED);
        tblPr.setTblLayout(tbll);
        //width
        TblWidth tblWidth1 = new TblWidth();
        tblWidth1.setType("dxa");
        tblWidth1.setW(BigInteger.valueOf(widthAvaliable));
        tblPr.setTblW(tblWidth1);
        //tblGrid
        TblGrid tblGrid = factory.createTblGrid();
        List<TblGridCol> gridCol = tblGrid.getGridCol();
        if (gridCol == null) {
            TblGridCol[] objects = ArrayUtil.newArray(TblGridCol.class, colsNum);
            gridCol = Arrays.asList(objects);
        }
        gridCol.forEach(e -> e.setW(BigInteger.valueOf(colWidth)));
        tbl.setTblPr(tblPr);
        tbl.setTblGrid(tblGrid);
        //border
        addBorders(tbl);
        //margin
        setTableCellMargin(tbl, "100", "" + colWidth / 10, "100", "" + colWidth / 10);
        for (int i = 0; i < rowNum + 1; i++) {
            Tr dataTr = factory.createTr();
            for (int j = 0; j < colsNum; j++) {
                Tc tc = factory.createTc();
                //设置单元格样式
                TcPr tcPr = factory.createTcPr();
                tcPr.setTcFitText(null);
                setTcWidth(tc, colWidth + "");
                dataTr.getContent().add(tc);
                //渲染数据
                P p = factory.createP();
                RPr rPr = null;
                //插入内容
                String content;
                //首行渲染标题
                if (i == 0) {
                    Field title = effectiveField.get(j);
                    ApiModelProperty annotation = title.getAnnotation(ApiModelProperty.class);
                    if (annotation != null) {
                        content = annotation.value();
                    } else {
                        content = title.getName();
                    }
                    //首行标题加粗
                    rPr = factory.createRPr();
                    rPr.setB(factory.createBooleanDefaultTrue());
                    Docx4jUtil.setFontFamily(rPr, "黑体", "Times New Roman");
                } else {
                    //渲染数据
                    T t = data.get(i - 1);
                    Field declaredField = null;
                    Field title = effectiveField.get(j);
                    Field[] declaredFields = t.getClass().getDeclaredFields();
                    for (Field field : declaredFields) {
                        if (field.getName().equals(title.getName())) {
                            declaredField = field;
                            break;
                        }
                    }
                    if (declaredField == null) {
                        throw new RuntimeException("未找到字段" + title.getName());
                    }
                    //找到正确的那个字段
                    declaredField.setAccessible(true);
                    Docx4jTblField annotation = declaredField.getAnnotation(Docx4jTblField.class);
                    if (annotation != null && annotation.type().equals(Docx4jNodeType.IMAGE)) {
                        //当前字段是图片
                        try {
                            Drawing imagePart = createImagePart(wordPackage, StrUtil.toString(declaredField.get(t)), declaredField.getName(), Math.toIntExact(Math.round(colWidth * 0.85)));
                            org.docx4j.wml.R run = factory.createR();
                            //居中

                            run.getContent().add(imagePart);
                            p.getContent().add(run);
                            tc.getContent().add(p);
                            continue;
                        } catch (Exception e) {
                            log.warn("获取图片失败,展示原始url,错误日志:{}", e.getMessage());
                        }
                    }
                    Object o = declaredField.get(t);
                    content = o == null ? "" : o.toString();
                }
                //插入数据
                Text t = factory.createText();
                t.setValue(content);
                org.docx4j.wml.R run = factory.createR();
                run.setRPr(rPr);
                run.getContent().add(t);
                p.getContent().add(run);
                tc.getContent().add(p);
            }
            tbl.getContent().add(dataTr);
        }
        log.info("创建表格,{}行,{}列,总宽度:{},列宽:{}", rowNum, colsNum, widthAvaliable, colWidth);
        return tbl;
    }


    /**
     * 创建表格(默认水平居中,垂直居中)  此方式 不好改样式
     */
    public static Tbl createTable(int rowNum, int colsNum, int[] widthArr) throws Exception {
        colsNum = Math.max(1, Math.min(colsNum, widthArr.length));
        rowNum = Math.max(1, rowNum);
        Tbl tbl = new Tbl();
        StringBuffer tblSb = new StringBuffer();
        tblSb.append("<w:tblPr ").append(Namespaces.W_NAMESPACE_DECLARATION).append(">");
        tblSb.append("<w:tblStyle w:val=\"TableGrid\"/>");
        tblSb.append("<w:tblW w:w=\"0\" w:type=\"auto\"/>");
        // 上边框
        tblSb.append("<w:tblBorders>");
        tblSb.append("<w:top w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        // 左边框
        tblSb.append("<w:left w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        // 下边框
        tblSb.append("<w:bottom w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        // 右边框
        tblSb.append("<w:right w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        tblSb.append("<w:insideH w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        tblSb.append("<w:insideV w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
        tblSb.append("</w:tblBorders>");
        tblSb.append("</w:tblPr>");
        TblPr tblPr = null;
        tblPr = (TblPr) XmlUtils.unmarshalString(tblSb.toString());
        Jc jc = new Jc();
        // 单元格居中对齐
        jc.setVal(JcEnumeration.CENTER);
        tblPr.setJc(jc);

        tbl.setTblPr(tblPr);

        // 设定各单元格宽度
        TblGrid tblGrid = new TblGrid();
        tbl.setTblGrid(tblGrid);
        for (int i = 0; i < colsNum; i++) {
            TblGridCol gridCol = new TblGridCol();
            gridCol.setW(BigInteger.valueOf(widthArr[i]));
            tblGrid.getGridCol().add(gridCol);
        }
        // 新增行
        for (int j = 0; j < rowNum; j++) {
            Tr tr = new Tr();
            tbl.getContent().add(tr);
            // 列
            for (int i = 0; i < colsNum; i++) {
                Tc tc = new Tc();
                tr.getContent().add(tc);

                TcPr tcPr = new TcPr();
                TblWidth cellWidth = new TblWidth();
                //cellWidth.setType("dxa");
                cellWidth.setType("pct");
                cellWidth.setW(BigInteger.valueOf(widthArr[i]));
                tcPr.setTcW(cellWidth);
                tc.setTcPr(tcPr);

                // 垂直居中
                setTcVAlign(tc, STVerticalJc.CENTER);
                P p = new P();
                PPr pPr = new PPr();
                pPr.setJc(jc);
                //设置段后距离
                PPrBase.Spacing spacing = new PPrBase.Spacing();
                spacing.setLineRule(STLineSpacingRule.AUTO);
                pPr.setSpacing(spacing);
                p.setPPr(pPr);
                R run = new R();
                p.getContent().add(run);
                tc.getContent().add(p);
            }
        }
        return tbl;
    }

    /**
     * 设置边框样式,全部边框
     *
     * @param table 需要设置表格边框的单元格
     */
    public static void addBorders(Tbl table) {
        table.setTblPr(new TblPr());// 必须设置一个TblPr，否则最后会报空指针异常

        CTBorder border = new CTBorder();
        border.setColor("auto");
        border.setSz(new BigInteger("4"));
        border.setSpace(new BigInteger("0"));
        border.setVal(STBorder.SINGLE);

        TblBorders borders = new TblBorders();
        borders.setBottom(border);
        borders.setLeft(border);
        borders.setRight(border);
        borders.setTop(border);
        borders.setInsideH(border);
        borders.setInsideV(border);

// 获取其内部的TblPr属性设置属性
        table.getTblPr().setTblBorders(borders);
    }

    /**
     * 表格增加边框 可以设置上下左右四个边框样式以及横竖水平线样式
     */
    public static void setTblBorders(TblPr tblPr, CTBorder topBorder, CTBorder rightBorder, CTBorder bottomBorder,
                                     CTBorder leftBorder, CTBorder hBorder, CTBorder vBorder) {
        TblBorders borders = tblPr.getTblBorders();
        if (borders == null) {
            borders = new TblBorders();
            tblPr.setTblBorders(borders);
        }
        if (topBorder != null) {
            borders.setTop(topBorder);
        }
        if (rightBorder != null) {
            borders.setRight(rightBorder);
        }
        if (bottomBorder != null) {
            borders.setBottom(bottomBorder);
        }
        if (leftBorder != null) {
            borders.setLeft(leftBorder);
        }
        if (hBorder != null) {
            borders.setInsideH(hBorder);
        }
        if (vBorder != null) {
            borders.setInsideV(vBorder);
        }
    }

    /**
     * 设置表格水平对齐方式(仅对表格起作用, 单元格不一定水平对齐)
     */
    public static void setTblJcAlign(Tbl tbl, JcEnumeration jcType) {
        if (jcType != null) {
            TblPr tblPr = getTblPr(tbl);
            Jc jc = tblPr.getJc();
            if (jc == null) {
                jc = new Jc();
                tblPr.setJc(jc);
            }
            jc.setVal(jcType);
        }
    }

    /**
     * 设置表格水平对齐方式(包括单元格), 只对该方法前面产生的单元格起作用
     */
    public static void setTblAllJcAlign(Tbl tbl, JcEnumeration jcType) {
        if (jcType != null) {
            setTblJcAlign(tbl, jcType);
            List<Tr> trList = getTblAllTr(tbl);
            for (Tr tr : trList) {
                List<Tc> tcList = getTrAllCell(tr);
                for (Tc tc : tcList) {
                    setTcJcAlign(tc, jcType);
                }
            }
        }
    }

    /**
     * 设置表格垂直对齐方式(包括单元格), 只对该方法前面产生的单元格起作用
     */
    public static void setTblAllVAlign(Tbl tbl, STVerticalJc vAlignType) {
        if (vAlignType != null) {
            List<Tr> trList = getTblAllTr(tbl);
            for (Tr tr : trList) {
                List<Tc> tcList = getTrAllCell(tr);
                for (Tc tc : tcList) {
                    setTcVAlign(tc, vAlignType);
                }
            }
        }
    }

    /**
     * 设置单元格Margin
     */
    public static void setTableCellMargin(Tbl tbl, String top, String right, String bottom, String left) {
        TblPr tblPr = getTblPr(tbl);
        CTTblCellMar cellMar = tblPr.getTblCellMar();
        if (cellMar == null) {
            cellMar = new CTTblCellMar();
            tblPr.setTblCellMar(cellMar);
        }
        if (StringUtils.isNotBlank(top)) {
            TblWidth topW = new TblWidth();
            topW.setW(new BigInteger(top));
            topW.setType("dxa");
            cellMar.setTop(topW);
        }
        if (StringUtils.isNotBlank(right)) {
            TblWidth rightW = new TblWidth();
            rightW.setW(new BigInteger(right));
            rightW.setType("dxa");
            cellMar.setRight(rightW);
        }
        if (StringUtils.isNotBlank(bottom)) {
            TblWidth btW = new TblWidth();
            btW.setW(new BigInteger(bottom));
            btW.setType("dxa");
            cellMar.setBottom(btW);
        }
        if (StringUtils.isNotBlank(left)) {
            TblWidth leftW = new TblWidth();
            leftW.setW(new BigInteger(left));
            leftW.setType("dxa");
            cellMar.setLeft(leftW);
        }
    }

    /**
     * 得到表格所有的行
     */
    public static List<Tr> getTblAllTr(Tbl tbl) {
        List<Object> objList = getAllElementFromObject(tbl, Tr.class);
        List<Tr> trList = new ArrayList<Tr>();
        if (objList == null) {
            return trList;
        }
        for (Object obj : objList) {
            if (obj instanceof Tr) {
                Tr tr = (Tr) obj;
                trList.add(tr);
            }
        }
        return trList;

    }

    /**
     * 设置tr高度
     */
    public static void setTrHeight(Tr tr, String heigth) {
        TrPr trPr = getTrPr(tr);
        CTHeight ctHeight = new CTHeight();
        ctHeight.setVal(new BigInteger(heigth));
        TrHeight trHeight = new TrHeight(ctHeight);
        trHeight.set(trPr);
    }

    /**
     * 在表格指定位置新增一行, 默认居中
     */
    public static void addTrByIndex(Tbl tbl, int index) {
        addTrByIndex(tbl, index, STVerticalJc.CENTER, JcEnumeration.CENTER);
    }

    /**
     * 在表格指定位置新增一行(默认按表格定义的列数添加)
     */
    public static void addTrByIndex(Tbl tbl, int index, STVerticalJc vAlign, JcEnumeration hAlign) {
        TblGrid tblGrid = tbl.getTblGrid();
        Tr tr = new Tr();
        if (tblGrid != null) {
            List<TblGridCol> gridList = tblGrid.getGridCol();
            for (TblGridCol tblGridCol : gridList) {
                Tc tc = new Tc();
                setTcWidth(tc, tblGridCol.getW().toString());
                if (vAlign != null) {
                    // 垂直居中
                    setTcVAlign(tc, vAlign);
                }
                P p = new P();
                if (hAlign != null) {
                    PPr pPr = new PPr();
                    Jc jc = new Jc();
                    // 单元格居中对齐
                    jc.setVal(hAlign);
                    pPr.setJc(jc);
                    p.setPPr(pPr);
                }
                R run = new R();
                p.getContent().add(run);
                tc.getContent().add(p);
                tr.getContent().add(tc);
            }
        } else {
            // 大部分情况都不会走到这一步
            Tr firstTr = getTblAllTr(tbl).get(0);
            int cellSize = getTcCellSizeWithMergeNum(firstTr);
            for (int i = 0; i < cellSize; i++) {
                Tc tc = new Tc();
                if (vAlign != null) {
                    // 垂直居中
                    setTcVAlign(tc, vAlign);
                }
                P p = new P();
                if (hAlign != null) {
                    PPr pPr = new PPr();
                    Jc jc = new Jc();
                    // 单元格居中对齐
                    jc.setVal(hAlign);
                    pPr.setJc(jc);
                    p.setPPr(pPr);
                }
                R run = new R();
                p.getContent().add(run);
                tc.getContent().add(p);
                tr.getContent().add(tc);
            }
        }
        if (index >= 0 && index < tbl.getContent().size()) {
            tbl.getContent().add(index, tr);
        } else {
            tbl.getContent().add(tr);
        }
    }

    /**
     * 得到行的列数
     */
    public static int getTcCellSizeWithMergeNum(Tr tr) {
        int cellSize = 1;
        List<Tc> tcList = getTrAllCell(tr);
        if (tcList == null || tcList.size() == 0) {
            return cellSize;
        }
        cellSize = tcList.size();
        for (Tc tc : tcList) {
            TcPr tcPr = getTcPr(tc);
            GridSpan gridSpan = tcPr.getGridSpan();
            if (gridSpan != null) {
                cellSize += gridSpan.getVal().intValue() - 1;
            }
        }
        return cellSize;
    }

    /**
     * 删除指定行 删除后行数减一
     */
    public static boolean removeTrByIndex(Tbl tbl, int index) {
        boolean flag = false;
        if (index < 0) {
            return flag;
        }
        List<Object> objList = tbl.getContent();
        if (objList == null) {
            return flag;
        }
        int k = -1;
        for (int i = 0, len = objList.size(); i < len; i++) {
            Object obj = XmlUtils.unwrap(objList.get(i));
            if (obj instanceof Tr) {
                k++;
                if (k == index) {
                    tbl.getContent().remove(i);
                    flag = true;
                    break;
                }
            }
        }
        return flag;
    }

    public static TrPr getTrPr(Tr tr) {
        TrPr trPr = tr.getTrPr();
        if (trPr == null) {
            trPr = new TrPr();
            tr.setTrPr(trPr);
        }
        return trPr;
    }

    /**
     * 隐藏行(只对表格中间的部分起作用,不包括首尾行)
     */
    public static void setTrHidden(Tr tr, boolean hidden) {
        List<Tc> tcList = getTrAllCell(tr);
        for (Tc tc : tcList) {
            setTcHidden(tc, hidden);
        }
    }

    /**
     * 设置单元格宽度
     */
    public static void setTcWidth(Tc tc, String width) {
        if (StringUtils.isNotBlank(width)) {
            TcPr tcPr = getTcPr(tc);
            TblWidth tcW = tcPr.getTcW();
            if (tcW == null) {
                tcW = new TblWidth();
                tcPr.setTcW(tcW);
            }
            tcW.setW(new BigInteger(width));
            tcW.setType("dxa");
            //tcW.setType("pct");
        }
    }

    /**
     * 隐藏单元格内容
     */
    public static void setTcHidden(Tc tc, boolean hidden) {
        List<P> pList = getTcAllP(tc);
        for (P p : pList) {
            PPr ppr = getPPr(p);
            List<Object> objRList = getAllElementFromObject(p, R.class);
            if (objRList == null) {
                continue;
            }
            for (Object objR : objRList) {
                if (objR instanceof R) {
                    R r = (R) objR;
                    RPr rpr = getRPr(r);
                    setRPrVanishStyle(rpr, hidden);
                }
            }
            setParaVanish(ppr, hidden);
        }
    }

    public static List<P> getTcAllP(Tc tc) {
        List<Object> objList = getAllElementFromObject(tc, P.class);
        List<P> pList = new ArrayList<P>();
        if (objList == null) {
            return pList;
        }
        for (Object obj : objList) {
            if (obj instanceof P) {
                P p = (P) obj;
                pList.add(p);
            }
        }
        return pList;
    }

    public static TcPr getTcPr(Tc tc) {
        TcPr tcPr = tc.getTcPr();
        if (tcPr == null) {
            tcPr = new TcPr();
            tc.setTcPr(tcPr);
        }
        return tcPr;
    }

    /**
     * 设置单元格垂直对齐方式
     */
    public static void setTcVAlign(Tc tc, STVerticalJc vAlignType) {
        if (vAlignType != null) {
            TcPr tcPr = getTcPr(tc);
            CTVerticalJc vAlign = new CTVerticalJc();
            vAlign.setVal(vAlignType);
            tcPr.setVAlign(vAlign);
        }
    }

    /**
     * 设置单元格水平对齐方式
     */
    public static void setTcJcAlign(Tc tc, JcEnumeration jcType) {
        if (jcType != null) {
            List<P> pList = getTcAllP(tc);
            for (P p : pList) {
                setParaJcAlign(p, jcType);
            }
        }
    }

    public static RPr getRPr(R r) {
        RPr rpr = r.getRPr();
        if (rpr == null) {
            rpr = new RPr();
            r.setRPr(rpr);
        }
        return rpr;
    }

    /**
     * 获取所有的单元格
     */
    public static List<Tc> getTrAllCell(Tr tr) {
        List<Object> objList = getAllElementFromObject(tr, Tc.class);
        List<Tc> tcList = new ArrayList<Tc>();
        if (objList == null) {
            return tcList;
        }
        for (Object tcObj : objList) {
            if (tcObj instanceof Tc) {
                Tc objTc = (Tc) tcObj;
                tcList.add(objTc);
            }
        }
        return tcList;
    }

    /**
     * 获取单元格内容
     */
    public static String getTcContent(Tc tc) throws Exception {
        return getElementContent(tc);
    }

    /**
     * 设置单元格内容,content为null则清除单元格内容
     */
    public static void setTcContent(Tc tc, RPr rpr, String content) {
        List<Object> pList = tc.getContent();
        P p = null;
        if (pList != null && pList.size() > 0) {
            if (pList.get(0) instanceof P) {
                p = (P) pList.get(0);
            }
        } else {
            p = new P();
            tc.getContent().add(p);
        }
        R run = null;
        List<Object> rList = p.getContent();
        if (rList != null && rList.size() > 0) {
            for (int i = 0, len = rList.size(); i < len; i++) {
                // 清除内容(所有的r
                p.getContent().remove(0);
            }
        }
        run = new R();
        p.getContent().add(run);
        if (content != null) {
            String[] contentArr = content.split("\n");
            Text text = new Text();
            text.setSpace("preserve");
            text.setValue(contentArr[0]);
            run.setRPr(rpr);
            run.getContent().add(text);

            for (int i = 1, len = contentArr.length; i < len; i++) {
                Br br = new Br();
                run.getContent().add(br);// 换行
                text = new Text();
                text.setSpace("preserve");
                text.setValue(contentArr[i]);
                run.setRPr(rpr);
                run.getContent().add(text);
            }
        }
    }

    /**
     * 设置单元格内容,content为null则清除单元格内容  支持设置字体
     */
    public static void setTcContent(Tc tc, String content, String cnFonts, String enFonts) {
        ObjectFactory factory = Context.getWmlObjectFactory();
        RPr rpr = factory.createRPr();
        setFontFamily(rpr, cnFonts, enFonts);
        if (StringUtils.isNotBlank(cnFonts) || StringUtils.isNotBlank(enFonts)) {
            RFonts rf = rpr.getRFonts();
            if (rf == null) {
                rf = new RFonts();
                rpr.setRFonts(rf);
            }
            if (cnFonts != null) {
                rf.setEastAsia(cnFonts);
            }
            if (enFonts != null) {
                rf.setAscii(enFonts);
            }
        }
        List<Object> pList = tc.getContent();
        P p = null;
        if (pList != null && pList.size() > 0) {
            if (pList.get(0) instanceof P) {
                p = (P) pList.get(0);
            }
        } else {
            p = new P();
            tc.getContent().add(p);
        }
        R run = null;
        List<Object> rList = p.getContent();
        if (rList != null && rList.size() > 0) {
            for (int i = 0, len = rList.size(); i < len; i++) {
                // 清除内容(所有的r
                p.getContent().remove(0);
            }
        }
        run = new R();
        p.getContent().add(run);
        if (content != null) {
            String[] contentArr = content.split("\n");
            Text text = new Text();
            text.setSpace("preserve");
            text.setValue(contentArr[0]);
            run.setRPr(rpr);
            run.getContent().add(text);

            for (int i = 1, len = contentArr.length; i < len; i++) {
                Br br = new Br();
                run.getContent().add(br);// 换行
                text = new Text();
                text.setSpace("preserve");
                text.setValue(contentArr[i]);
                run.setRPr(rpr);
                run.getContent().add(text);
            }
        }
    }

    /**
     * 设置单元格内容,content为null则清除单元格内容
     */
    public static void removeTcContent(Tc tc) {
        List<Object> pList = tc.getContent();
        P p = null;
        if (pList != null && pList.size() > 0) {
            if (pList.get(0) instanceof P) {
                p = (P) pList.get(0);
            }
        } else {
            return;
        }
        List<Object> rList = p.getContent();
        if (rList != null && rList.size() > 0) {
            for (int i = 0, len = rList.size(); i < len; i++) {
                // 清除内容(所有的r
                p.getContent().remove(0);
            }
        }
    }

    /**
     * 删除指定位置的表格
     *
     * @deprecated
     */
    public static void deleteTableByIndex2(WordprocessingMLPackage wordMLPackage, int index) throws Exception {
        if (index < 0) {
            return;
        }
        final String xpath = "(//w:tbl)[" + index + "]";
        final List<Object> jaxbNodes = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(xpath, true);
        if (jaxbNodes != null && jaxbNodes.size() > 0) {
            wordMLPackage.getMainDocumentPart().getContent().remove(jaxbNodes.get(0));
        }
    }

    /**
     * 获取NodeList
     *
     * @deprecated
     */
    public static List<Object> getObjectByXpath(WordprocessingMLPackage wordMLPackage, String xpath) throws Exception {
        final List<Object> jaxbNodes = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(xpath, true);
        return jaxbNodes;
    }

    /*------------------------------------Word 段落相关---------------------------------------------------  */

    /**
     * 只删除单独的段落，不包括表格内或其他内的段落
     */
    public static boolean removeParaByIndex(WordprocessingMLPackage wordMLPackage, int index) {
        boolean flag = false;
        if (index < 0) {
            return flag;
        }
        List<Object> objList = wordMLPackage.getMainDocumentPart().getContent();
        if (objList == null) {
            return flag;
        }
        int k = -1;
        for (int i = 0, len = objList.size(); i < len; i++) {
            if (objList.get(i) instanceof P) {
                k++;
                if (k == index) {
                    wordMLPackage.getMainDocumentPart().getContent().remove(i);
                    flag = true;
                    break;
                }
            }
        }
        return flag;
    }

    /**
     * 设置段落水平对齐方式
     */
    public static void setParaJcAlign(P paragraph, JcEnumeration hAlign) {
        if (hAlign != null) {
            PPr pprop = paragraph.getPPr();
            if (pprop == null) {
                pprop = new PPr();
                paragraph.setPPr(pprop);
            }
            Jc align = new Jc();
            align.setVal(hAlign);
            pprop.setJc(align);
        }
    }

    public static void setPAlign(P p, JcEnumeration jcEnumeration) {
        PPr pPr = p.getPPr();
        if (pPr == null) {
            pPr = factory.createPPr();
        }
        Jc jc = new Jc();
        jc.setVal(jcEnumeration);
        pPr.setJc(jc);
    }

    /**
     * 设置段落内容
     */
    public static void setParaRContent(P p, RPr runProperties, String content) {
        R run = null;
        List<Object> rList = p.getContent();
        if (rList != null && rList.size() > 0) {
            for (int i = 0, len = rList.size(); i < len; i++) {
                // 清除内容(所有的r
                p.getContent().remove(0);
            }
        }
        run = new R();
        p.getContent().add(run);
        if (content != null) {
            String[] contentArr = content.split("\n");
            Text text = new Text();
            text.setSpace("preserve");
            text.setValue(contentArr[0]);
            run.setRPr(runProperties);
            run.getContent().add(text);

            for (int i = 1, len = contentArr.length; i < len; i++) {
                Br br = new Br();
                run.getContent().add(br);// 换行
                text = new Text();
                text.setSpace("preserve");
                text.setValue(contentArr[i]);
                run.setRPr(runProperties);
                run.getContent().add(text);
            }
        }
    }

    /**
     * 添加段落内容
     */
    public static void appendParaRContent(P p, RPr runProperties, String content) {
        if (content != null) {
            R run = new R();
            p.getContent().add(run);
            String[] contentArr = content.split("\n");
            Text text = new Text();
            text.setSpace("preserve");
            text.setValue(contentArr[0]);
            run.setRPr(runProperties);
            run.getContent().add(text);

            for (int i = 1, len = contentArr.length; i < len; i++) {
                Br br = new Br();
                run.getContent().add(br);// 换行
                text = new Text();
                text.setSpace("preserve");
                text.setValue(contentArr[i]);
                run.setRPr(runProperties);
                run.getContent().add(text);
            }
        }
    }

    /**
     * 添加图片到段落
     */
    public static void addImageToPara(WordprocessingMLPackage wordMLPackage, P paragraph,
                                      String filePath, String content, RPr rpr, String altText, int id1, int id2) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        R run = factory.createR();
        if (content != null) {
            Text text = factory.createText();
            text.setValue(content);
            text.setSpace("preserve");
            run.setRPr(rpr);
            run.getContent().add(text);
        }

        InputStream is = new FileInputStream(filePath);
        byte[] bytes = IOUtils.toByteArray(is);
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
        Inline inline = imagePart.createImageInline(filePath, altText, id1, id2, false);
        Drawing drawing = factory.createDrawing();
        drawing.getAnchorOrInline().add(inline);
        run.getContent().add(drawing);
        paragraph.getContent().add(run);
    }

    /**
     * 生成图片,自适应页面大小
     */
    public static Drawing createImagePart(WordprocessingMLPackage wordMLPackage,
                                          String url, String altText) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        byte[] bytes;
        if (url.startsWith("http")) {
            try {
                log.info("正在加载网络资源文件：{}", url);
                long now = SystemClock.now();
                bytes = HttpUtil.downloadBytes(url);
                log.info("网络资源文件加载耗时：{}", SystemClock.now() - now);
            } catch (Exception e) {
                throw new RuntimeException("url:[" + url + "]地址资源下载错误");
            }

        } else {
            File file = new File(url);
            FileInputStream fileInputStream = new FileInputStream(file);
            bytes = IoUtil.readBytes(fileInputStream);
        }
        //fixme 此处图片资源如果获取不了,会抛出错误,影响使用,建议使用默认的图片占位
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
        Inline inline = imagePart.createImageInline(url, altText, RandomUtil.randomInt(1, 10000), RandomUtil.randomInt(10000, 100000), false);
        Drawing drawing = factory.createDrawing();
        drawing.getAnchorOrInline().add(inline);
        return drawing;
    }

    @SneakyThrows
    public static CTAltChunk creatHtmlChunk(WordprocessingMLPackage wordMLPackage, String htmlStr, String htmlPartName) {
        //  创建并设置AlternativeFormatInputPart
        AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(new PartName("/hw" + htmlPartName + ".html")); //CAUTION: each html part needs a new name!!
        afiPart.setBinaryData(htmlStr.getBytes("UTF-8"));
        afiPart.setContentType(new ContentType("text/html"));
        //  创建CTAltChunk
        Relationship altChunkRel = wordMLPackage.getMainDocumentPart().addTargetPart(afiPart);
        CTAltChunk ac = Context.getWmlObjectFactory().createCTAltChunk();
        ac.setId(altChunkRel.getId());
        return ac;
    }

    /**
     * 生成图片,限制最大宽度
     */
    public static Drawing createImagePart(WordprocessingMLPackage wordMLPackage,
                                          String url, String altText, int maxWidth) throws Exception {
        ObjectFactory factory = Context.getWmlObjectFactory();
        long now = SystemClock.now();
        byte[] bytes;
        if (DocumentContext.containsKey(url)) {
            log.info("网络资源图片:{}命中缓存", url);
            bytes = DocumentContext.getCache(url);
        } else {
            bytes = HttpUtil.downloadBytes(url);
            log.info("加载网络资源图片:{},耗时:{}ms", url, SystemClock.now() - now);
            DocumentContext.setCache(url, bytes);
        }

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
        Inline inline = imagePart.createImageInline(url, altText, RandomUtil.randomInt(1, 10000), RandomUtil.randomInt(10000, 100000), false, maxWidth);
        Drawing drawing = factory.createDrawing();
        drawing.getAnchorOrInline().add(inline);
        return drawing;
    }

    /**
     * 段落添加Br 页面Break(分页符)
     */
    public static void addPageBreak(P para, STBrType sTBrType) {
        Br breakObj = new Br();
        breakObj.setClear(STBrClear.NONE);
        breakObj.setType(sTBrType);
        para.getContent().add(breakObj);
    }

    /**
     * 设置段落是否禁止行号(禁止用于当前行号)
     */
    public static void setParagraphSuppressLineNum(P p) {
        PPr ppr = getPPr(p);
        BooleanDefaultTrue line = ppr.getSuppressLineNumbers();
        if (line == null) {
            line = new BooleanDefaultTrue();
        }
        line.setVal(true);
        ppr.setSuppressLineNumbers(line);
    }

    /**
     * 设置段落底纹(对整段文字起作用)
     */
    public static void setParagraphShdStyle(P p, STShd shdType, String shdColor) {
        PPr ppr = getPPr(p);
        CTShd ctShd = ppr.getShd();
        if (ctShd == null) {
            ctShd = new CTShd();
        }
        if (StringUtils.isNotBlank(shdColor)) {
            ctShd.setColor(shdColor);
        }
        if (shdType != null) {
            ctShd.setVal(shdType);
        }
        ppr.setShd(ctShd);
    }

    /**
     * @param isSpace           是否设置段前段后值
     * @param before            段前磅数
     * @param after             段后磅数
     * @param beforeLines       段前行数
     * @param afterLines        段后行数
     * @param isLine            是否设置行距
     * @param lineValue         行距值
     * @param sTLineSpacingRule 自动auto 固定exact 最小 atLeast 1磅=20 1行=100 单倍行距=240
     */
    public static void setParagraphSpacing(P p, boolean isSpace, String before, String after, String beforeLines,
                                           String afterLines, boolean isLine, String lineValue, STLineSpacingRule sTLineSpacingRule) {
        PPr pPr = getPPr(p);
        Spacing spacing = pPr.getSpacing();
        if (spacing == null) {
            spacing = new Spacing();
            pPr.setSpacing(spacing);
        }
        if (isSpace) {
            if (StringUtils.isNotBlank(before)) {
                // 段前磅数
                spacing.setBefore(new BigInteger(before));
            }
            if (StringUtils.isNotBlank(after)) {
                // 段后磅数
                spacing.setAfter(new BigInteger(after));
            }
            if (StringUtils.isNotBlank(beforeLines)) {
                // 段前行数
                spacing.setBeforeLines(new BigInteger(beforeLines));
            }
            if (StringUtils.isNotBlank(afterLines)) {
                // 段后行数
                spacing.setAfterLines(new BigInteger(afterLines));
            }
        }
        if (isLine) {
            if (StringUtils.isNotBlank(lineValue)) {
                spacing.setLine(new BigInteger(lineValue));
            }
            if (sTLineSpacingRule != null) {
                spacing.setLineRule(sTLineSpacingRule);
            }
        }
    }

    /**
     * 设置段落缩进信息 1厘米≈567
     */
    public static void setParagraphIndInfo(P p, String firstLine, String firstLineChar, String hanging,
                                           String hangingChar, String right, String rigthChar, String left, String leftChar) {
        PPr ppr = getPPr(p);
        Ind ind = ppr.getInd();
        if (ind == null) {
            ind = new Ind();
            ppr.setInd(ind);
        }
        if (StringUtils.isNotBlank(firstLine)) {
            ind.setFirstLine(new BigInteger(firstLine));
        }
        if (StringUtils.isNotBlank(firstLineChar)) {
            ind.setFirstLineChars(new BigInteger(firstLineChar));
        }
        if (StringUtils.isNotBlank(hanging)) {
            ind.setHanging(new BigInteger(hanging));
        }
        if (StringUtils.isNotBlank(hangingChar)) {
            ind.setHangingChars(new BigInteger(hangingChar));
        }
        if (StringUtils.isNotBlank(left)) {
            ind.setLeft(new BigInteger(left));
        }
        if (StringUtils.isNotBlank(leftChar)) {
            ind.setLeftChars(new BigInteger(leftChar));
        }
        if (StringUtils.isNotBlank(right)) {
            ind.setRight(new BigInteger(right));
        }
        if (StringUtils.isNotBlank(rigthChar)) {
            ind.setRightChars(new BigInteger(rigthChar));
        }
    }

    public static PPr getPPr(P p) {
        PPr ppr = p.getPPr();
        if (ppr == null) {
            ppr = new PPr();
            p.setPPr(ppr);
        }
        return ppr;
    }

    public static ParaRPr getParaRPr(PPr ppr) {
        ParaRPr parRpr = ppr.getRPr();
        if (parRpr == null) {
            parRpr = new ParaRPr();
            ppr.setRPr(parRpr);
        }
        return parRpr;

    }

    public static void setParaVanish(PPr ppr, boolean isVanish) {
        ParaRPr parRpr = getParaRPr(ppr);
        BooleanDefaultTrue vanish = parRpr.getVanish();
        if (vanish != null) {
            vanish.setVal(isVanish);
        } else {
            vanish = new BooleanDefaultTrue();
            parRpr.setVanish(vanish);
            vanish.setVal(isVanish);
        }
    }

    /**
     * 设置段落边框样式
     */
    public static void setParagraghBorders(P p, CTBorder topBorder, CTBorder bottomBorder, CTBorder leftBorder,
                                           CTBorder rightBorder) {
        PPr ppr = getPPr(p);
        PBdr pBdr = new PBdr();
        if (topBorder != null) {
            pBdr.setTop(topBorder);
        }
        if (bottomBorder != null) {
            pBdr.setBottom(bottomBorder);
        }
        if (leftBorder != null) {
            pBdr.setLeft(leftBorder);
        }
        if (rightBorder != null) {
            pBdr.setRight(rightBorder);
        }
        ppr.setPBdr(pBdr);
    }

    /**
     * 设置字体信息
     */
    public static void setFontStyle(RPr runProperties, String cnFontFamily, String enFontFamily, String fontSize,
                                    String color) {
        setFontFamily(runProperties, cnFontFamily, enFontFamily);
        setFontSize(runProperties, fontSize);
        setFontColor(runProperties, color);
    }

    /**
     * 设置字体大小
     */
    public static void setFontSize(RPr runProperties, String fontSize) {
        if (StringUtils.isNotBlank(fontSize)) {
            HpsMeasure size = new HpsMeasure();
            size.setVal(new BigInteger(fontSize));
            runProperties.setSz(size);
            runProperties.setSzCs(size);
        }
    }

    /**
     * 设置字体
     */
    public static void setFontFamily(RPr runProperties, String cnFontFamily, String enFontFamily) {
        if (StringUtils.isNotBlank(cnFontFamily) || StringUtils.isNotBlank(enFontFamily)) {
            RFonts rf = runProperties.getRFonts();
            if (rf == null) {
                rf = new RFonts();
                runProperties.setRFonts(rf);
            }
            if (cnFontFamily != null) {
                rf.setEastAsia(cnFontFamily);
            }
            if (enFontFamily != null) {
                rf.setAscii(enFontFamily);
            }
        }
    }

    /**
     * 设置字体颜色
     */
    public static void setFontColor(RPr runProperties, String color) {
        if (color != null) {
            Color c = new Color();
            c.setVal(color);
            runProperties.setColor(c);
        }
    }

    /**
     * 设置字符边框
     */
    public static void addRPrBorderStyle(RPr runProperties, String size, STBorder bordType, String space,
                                         String color) {
        CTBorder value = new CTBorder();
        if (StringUtils.isNotBlank(color)) {
            value.setColor(color);
        }
        if (StringUtils.isNotBlank(size)) {
            value.setSz(new BigInteger(size));
        }
        if (StringUtils.isNotBlank(space)) {
            value.setSpace(new BigInteger(space));
        }
        if (bordType != null) {
            value.setVal(bordType);
        }
        runProperties.setBdr(value);
    }

    /**
     * 着重号
     */
    public static void addRPrEmStyle(RPr runProperties, STEm emType) {
        if (emType != null) {
            CTEm em = new CTEm();
            em.setVal(emType);
            runProperties.setEm(em);
        }
    }

    /**
     * 空心
     */
    public static void addRPrOutlineStyle(RPr runProperties) {
        BooleanDefaultTrue outline = new BooleanDefaultTrue();
        outline.setVal(true);
        runProperties.setOutline(outline);
    }

    /**
     * 设置上标下标
     */
    public static void addRPrcaleStyle(RPr runProperties, STVerticalAlignRun vAlign) {
        if (vAlign != null) {
            CTVerticalAlignRun value = new CTVerticalAlignRun();
            value.setVal(vAlign);
            runProperties.setVertAlign(value);
        }
    }

    /**
     * 设置字符间距缩进
     */
    public static void addRPrScaleStyle(RPr runProperties, int indent) {
        CTTextScale value = new CTTextScale();
        value.setVal(indent);
        runProperties.setW(value);
    }

    /**
     * 设置字符间距信息
     */
    public static void addRPrtSpacingStyle(RPr runProperties, int spacing) {
        CTSignedTwipsMeasure value = new CTSignedTwipsMeasure();
        value.setVal(BigInteger.valueOf(spacing));
        runProperties.setSpacing(value);
    }

    /**
     * 设置文本位置
     */
    public static void addRPrtPositionStyle(RPr runProperties, int position) {
        CTSignedHpsMeasure ctPosition = new CTSignedHpsMeasure();
        ctPosition.setVal(BigInteger.valueOf(position));
        runProperties.setPosition(ctPosition);
    }

    /**
     * 阴文
     */
    public static void addRPrImprintStyle(RPr runProperties) {
        BooleanDefaultTrue imprint = new BooleanDefaultTrue();
        imprint.setVal(true);
        runProperties.setImprint(imprint);
    }

    /**
     * 阳文
     */
    public static void addRPrEmbossStyle(RPr runProperties) {
        BooleanDefaultTrue emboss = new BooleanDefaultTrue();
        emboss.setVal(true);
        runProperties.setEmboss(emboss);
    }

    /**
     * 设置隐藏
     */
    public static void setRPrVanishStyle(RPr runProperties, boolean isVanish) {
        BooleanDefaultTrue vanish = runProperties.getVanish();
        if (vanish != null) {
            vanish.setVal(isVanish);
        } else {
            vanish = new BooleanDefaultTrue();
            vanish.setVal(isVanish);
            runProperties.setVanish(vanish);
        }
    }

    /**
     * 设置阴影
     */
    public static void addRPrShadowStyle(RPr runProperties) {
        BooleanDefaultTrue shadow = new BooleanDefaultTrue();
        shadow.setVal(true);
        runProperties.setShadow(shadow);
    }

    /**
     * 设置底纹
     */
    public static void addRPrShdStyle(RPr runProperties, STShd shdtype) {
        if (shdtype != null) {
            CTShd shd = new CTShd();
            shd.setVal(shdtype);
            runProperties.setShd(shd);
        }
    }

    /**
     * 设置突出显示文本
     */
    public static void addRPrHightLightStyle(RPr runProperties, String hightlight) {
        if (StringUtils.isNotBlank(hightlight)) {
            Highlight highlight = new Highlight();
            highlight.setVal(hightlight);
            runProperties.setHighlight(highlight);
        }
    }

    /**
     * 设置删除线样式
     */
    public static void addRPrStrikeStyle(RPr runProperties, boolean isStrike, boolean isDStrike) {
        // 删除线
        if (isStrike) {
            BooleanDefaultTrue strike = new BooleanDefaultTrue();
            strike.setVal(true);
            runProperties.setStrike(strike);
        }
        // 双删除线
        if (isDStrike) {
            BooleanDefaultTrue dStrike = new BooleanDefaultTrue();
            dStrike.setVal(true);
            runProperties.setDstrike(dStrike);
        }
    }

    /**
     * 加粗
     */
    public static void addRPrBoldStyle(RPr runProperties) {
        BooleanDefaultTrue b = new BooleanDefaultTrue();
        b.setVal(true);
        runProperties.setB(b);
    }

    /**
     * 倾斜
     */
    public static void addRPrItalicStyle(RPr runProperties) {
        BooleanDefaultTrue b = new BooleanDefaultTrue();
        b.setVal(true);
        runProperties.setI(b);
    }

    /**
     * 添加下划线
     */
    public static void addRPrUnderlineStyle(RPr runProperties, UnderlineEnumeration enumType) {
        U val = new U();
        val.setVal(enumType);
        runProperties.setU(val);
    }

    /*------------------------------------Word 相关---------------------------------------------------  */

    /**
     * 设置分节符 nextPage:下一页 continuous:连续 evenPage:偶数页 oddPage:奇数页
     */
    public static void setDocSectionBreak(WordprocessingMLPackage wordPackage, String sectValType) {
        if (StringUtils.isNotBlank(sectValType)) {
            SectPr sectPr = getDocSectPr(wordPackage);
            Type sectType = sectPr.getType();
            if (sectType == null) {
                sectType = new Type();
                sectPr.setType(sectType);
            }
            sectType.setVal(sectValType);
        }
    }

    /**
     * 设置页面背景色
     */
    public static void setDocumentBackGround(WordprocessingMLPackage wordPackage, ObjectFactory factory, String color)
            throws Exception {
        MainDocumentPart mdp = wordPackage.getMainDocumentPart();
        CTBackground bkground = mdp.getContents().getBackground();
        if (StringUtils.isNotBlank(color)) {
            if (bkground == null) {
                bkground = factory.createCTBackground();
                bkground.setColor(color);
            }
            mdp.getContents().setBackground(bkground);
        }
    }

    /**
     * 设置页面边框
     */
    public static void setDocumentBorders(WordprocessingMLPackage wordPackage, ObjectFactory factory, CTBorder top,
                                          CTBorder right, CTBorder bottom, CTBorder left) {
        SectPr sectPr = getDocSectPr(wordPackage);
        PgBorders pgBorders = sectPr.getPgBorders();
        if (pgBorders == null) {
            pgBorders = factory.createSectPrPgBorders();
            sectPr.setPgBorders(pgBorders);
        }
        if (top != null) {
            pgBorders.setTop(top);
        }
        if (right != null) {
            pgBorders.setRight(right);
        }
        if (bottom != null) {
            pgBorders.setBottom(bottom);
        }
        if (left != null) {
            pgBorders.setLeft(left);
        }
    }

    /**
     * 设置页面大小及纸张方向 landscape横向
     */
    public static void setDocumentSize(WordprocessingMLPackage wordPackage, ObjectFactory factory, String width,
                                       String height, STPageOrientation stValue) {
        SectPr sectPr = getDocSectPr(wordPackage);
        PgSz pgSz = sectPr.getPgSz();
        if (pgSz == null) {
            pgSz = factory.createSectPrPgSz();
            sectPr.setPgSz(pgSz);
        }
        if (StringUtils.isNotBlank(width)) {
            pgSz.setW(new BigInteger(width));
        }
        if (StringUtils.isNotBlank(height)) {
            pgSz.setH(new BigInteger(height));
        }
        if (stValue != null) {
            pgSz.setOrient(stValue);
        }
    }

    public static SectPr getDocSectPr(WordprocessingMLPackage wordPackage) {
        SectPr sectPr = wordPackage.getDocumentModel().getSections().get(0).getSectPr();
        return sectPr;
    }

    /**
     * @Description：设置页边距
     */
    public static void setDocMarginSpace(WordprocessingMLPackage wordPackage, ObjectFactory factory, String top,
                                         String left, String bottom, String right) {
        SectPr sectPr = getDocSectPr(wordPackage);
        PgMar pg = sectPr.getPgMar();
        if (pg == null) {
            pg = factory.createSectPrPgMar();
            sectPr.setPgMar(pg);
        }
        if (StringUtils.isNotBlank(top)) {
            pg.setTop(new BigInteger(top));
        }
        if (StringUtils.isNotBlank(bottom)) {
            pg.setBottom(new BigInteger(bottom));
        }
        if (StringUtils.isNotBlank(left)) {
            pg.setLeft(new BigInteger(left));
        }
        if (StringUtils.isNotBlank(right)) {
            pg.setRight(new BigInteger(right));
        }
    }

    /**
     * @param distance    :距正文距离 1厘米=567
     * @param start       :起始编号(0开始)
     * @param countBy     :行号间隔
     * @param restartType :STLineNumberRestart.CONTINUOUS(continuous连续编号)<br/>
     *                    STLineNumberRestart.NEW_PAGE(每页重新编号)<br/>
     *                    STLineNumberRestart.NEW_SECTION(每节重新编号)
     *                    设置行号
     */
    public static void setDocInNumType(WordprocessingMLPackage wordPackage, String countBy, String distance,
                                       String start, STLineNumberRestart restartType) {
        SectPr sectPr = getDocSectPr(wordPackage);
        CTLineNumber lnNumType = sectPr.getLnNumType();
        if (lnNumType == null) {
            lnNumType = new CTLineNumber();
            sectPr.setLnNumType(lnNumType);
        }
        if (StringUtils.isNotBlank(countBy)) {
            lnNumType.setCountBy(new BigInteger(countBy));
        }
        if (StringUtils.isNotBlank(distance)) {
            lnNumType.setDistance(new BigInteger(distance));
        }
        if (StringUtils.isNotBlank(start)) {
            lnNumType.setStart(new BigInteger(start));
        }
        if (restartType != null) {
            lnNumType.setRestart(restartType);
        }
    }

    /**
     * @Description：设置文字方向 tbRl 垂直
     */
    public static void setDocTextDirection(WordprocessingMLPackage wordPackage, String textDirection) {
        if (StringUtils.isNotBlank(textDirection)) {
            SectPr sectPr = getDocSectPr(wordPackage);
            TextDirection textDir = sectPr.getTextDirection();
            if (textDir == null) {
                textDir = new TextDirection();
                sectPr.setTextDirection(textDir);
            }
            textDir.setVal(textDirection);
        }
    }

    /**
     * @Description：设置word 垂直对齐方式(Word默认方式都是 " 顶端对齐 ")
     */
    public static void setDocVAlign(WordprocessingMLPackage wordPackage, STVerticalJc valignType) {
        if (valignType != null) {
            SectPr sectPr = getDocSectPr(wordPackage);
            CTVerticalJc valign = sectPr.getVAlign();
            if (valign == null) {
                valign = new CTVerticalJc();
                sectPr.setVAlign(valign);
            }
            valign.setVal(valignType);
        }
    }

    /**
     * @Description：获取文档的可用宽度
     */
    public static int getWritableWidth(WordprocessingMLPackage wordPackage) throws Exception {
        return wordPackage.getDocumentModel().getSections().get(0).getPageDimensions().getWritableWidthTwips();
    }


    /**
     * 把docx转成html
     *
     * @param docxFilePath
     * @param htmlPath
     * @throws Exception
     */
    public static void convertDocxToHtml(String docxFilePath, String htmlPath) throws Exception {

        WordprocessingMLPackage wordMLPackage = Docx4J.load(new java.io.File(docxFilePath));

        HTMLSettings htmlSettings = Docx4J.createHTMLSettings();
        String imageFilePath = htmlPath.substring(0, htmlPath.lastIndexOf("\\") + 1) + "/images";
        htmlSettings.setImageDirPath(imageFilePath);
        htmlSettings.setImageTargetUri("images");
        htmlSettings.setWmlPackage(wordMLPackage);

        String userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img,  ol, ul, li, table, caption, tbody, tfoot, thead, tr, th, td " +
                "{ margin: 0; padding: 0; border: 0;}" +
                "body {line-height: 1;} ";
        htmlSettings.setUserCSS(userCSS);
        htmlSettings.setStyleElementHandler((opcPackage, document, styleDefinition) -> {
            Element ret = null;
            if (styleDefinition != null && styleDefinition.length() > 0) {
                ret = document.createElement("style");
                ret.appendChild(document.createComment(styleDefinition));
            }

            return ret;
        });

        OutputStream os;

        os = new FileOutputStream(htmlPath);

        Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true);

        Docx4J.toHTML(htmlSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
        log.info("转换完成");
    }

    public static void docxToHtml(String fileUrl) throws Exception {
        String path = fileUrl.substring(0, fileUrl.indexOf("."));
        File file = new File(fileUrl);
        WordprocessingMLPackage wordMLPackage = Docx4J.load(file);
        HTMLSettings htmlSettings = Docx4J.createHTMLSettings();
        String imageFilePath = path + "/images/";
        if (!new File(imageFilePath).exists()) {
            new File(imageFilePath).mkdirs();
        }
        htmlSettings.setImageDirPath(imageFilePath);
        htmlSettings.setImageTargetUri("images");
        htmlSettings.setWmlPackage(wordMLPackage);
        String userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img,  ol, ul, li, table, caption, tbody, tfoot, thead, tr, th, td " +
                "{ margin: 0; padding: 0; border: 0;}" +
                "body {line-height: 1; padding: 30px;} ";
        userCSS = "body {padding: 30px;}";
//        htmlSettings.setUserCSS(userCSS); //
        htmlSettings.setStyleElementHandler(new ConversionHTMLStyleElementHandler() {
            @Override
            public Element createStyleElement(OpcPackage opcPackage, org.w3c.dom.Document document, String styleDefinition) {
                Element ret = null;
                if (styleDefinition != null && styleDefinition.length() > 0) {
                    ret = document.createElement("style");
                    ret.appendChild(document.createComment(styleDefinition));
                }

                return ret;
            }
        });
        OutputStream os = new FileOutputStream(path + "/" + file.getName().substring(0, file.getName().indexOf(".")) + ".html");
        Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true);
        Docx4J.toHTML(htmlSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
        log.info("转换完成");
    }

    /**
     * Convert HTML to DOCX(OPENXML).
     *
     * @param inputStream the source, a HTML file
     * @param docx        the target, a DOCX file
     */
    public static void html2Word(InputStream inputStream, File docx) {
        try {
            ObjectFactory factory = Context.getWmlObjectFactory();
            // Creates a WordprocessingMLPackage, using default page size and orientation,
            // default to A4 portrait.
            WordprocessingMLPackage pack = WordprocessingMLPackage.createPackage();
            // Convert XHTML + CSS to WordML content.
            // XHTML must be well formed XML.
            XHTMLImporterImpl importer = new XHTMLImporterImpl(pack);
            // Convert the well formed XHTML contained in file to a list of WML objects.
            List<Object> list = importer.convert(inputStream, null);
            MainDocumentPart mainPart = pack.getMainDocumentPart();
            // Add all WML objects to MainDocumentPart.
            mainPart.getContent().addAll(list);
            // create footer
            Ftr footer = createFooterWithPageNumber();
            FooterPart footerPart = new FooterPart();
            footerPart.setPackage(pack);
            footerPart.setJaxbElement(footer);
            Relationship footerRelation = mainPart.addTargetPart(footerPart);
            FooterReference footerRef = factory.createFooterReference();
            footerRef.setId(footerRelation.getId());
            footerRef.setType(HdrFtrRef.DEFAULT);
            SectPr sectPr = pack.getDocumentModel().getSections().get(0).getSectPr();
            sectPr.getEGHdrFtrReferences().add(footerRef);
            // create header
            Hdr header = createHeader("****有限公司");
            HeaderPart headerPart = new HeaderPart();
            headerPart.setPackage(pack);
            headerPart.setJaxbElement(header);
            Relationship headerRelation = mainPart.addTargetPart(headerPart);
            HeaderReference headerRef = factory.createHeaderReference();
            headerRef.setId(headerRelation.getId());
            headerRef.setType(HdrFtrRef.DEFAULT);
            sectPr.getEGHdrFtrReferences().add(headerRef);
            // set compatibilityMode to 15
            // to avoid Word 365/2016 saying "Compatibility Mode"
            DocumentSettingsPart settingsPart = mainPart.getDocumentSettingsPart(true);
            CTCompat compat = factory.createCTCompat();
            compat.setCompatSetting("compatibilityMode", "http://schemas.microsoft.com/office/word", "15");
            settingsPart.getContents().setCompat(compat);
            // save to a file
            pack.save(docx);
        } catch (Docx4JException e) {
            throw new RuntimeException("covert html to docx error.", e);
        }
    }


    /* 页脚 */
    public static void createFooter(String content, SectPr sectPr, WordprocessingMLPackage wordMLPackage) {
        createFooter(content, sectPr, HdrFtrRef.DEFAULT, wordMLPackage);
    }

    /* 页脚 */
    @SneakyThrows
    public static void createFooter(String content, SectPr sectPr, HdrFtrRef type, WordprocessingMLPackage wordMLPackage) {
        MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();
        FooterPart footerPart = new FooterPart(new PartName("/word/footer-" + type.value() + ".xml"));
        Ftr ftr = factory.createFtr();

        // Bind the header JAXB elements as representing their header parts
        footerPart.setJaxbElement(ftr);
        Relationship relationship = mainDocumentPart.addTargetPart(footerPart);
        wordMLPackage.getParts().put(footerPart);

        FooterReference footerReference = factory.createFooterReference();
        footerReference.setType(type);
        footerReference.setId(relationship.getId());
        P paragraph = factory.createP();
        // 处理页脚
        createHeaderFooterContent(paragraph, false, content);

        switch (HdrFtrRef.FIRST) {
            case FIRST:
                sectPr.setTitlePg(new BooleanDefaultTrue());
            case EVEN:
                DocumentSettingsPart documentSettingsPart = mainDocumentPart.getDocumentSettingsPart();
                CTSettings contents = documentSettingsPart.getContents();
                //奇偶不同时设置
                contents.setEvenAndOddHeaders(new BooleanDefaultTrue());
            default:

        }

        ftr.getContent().add(paragraph);
        sectPr.getEGHdrFtrReferences().add(footerReference);
    }

    @SneakyThrows
    public static void createHeader(String content, SectPr sectPr, WordprocessingMLPackage wordMLPackage) {
        createHeader(content, false, sectPr, HdrFtrRef.DEFAULT, wordMLPackage);
    }

    @SneakyThrows
    public static void createHeader(String content, boolean waterMark, SectPr sectPr, WordprocessingMLPackage wordMLPackage) {
        createHeader(content, waterMark, sectPr, HdrFtrRef.DEFAULT, wordMLPackage);
    }

    @SneakyThrows
    public static void createHeader(String content, boolean waterMark, SectPr sectPr, HdrFtrRef type, WordprocessingMLPackage wordMLPackage) {
        MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();
        HeaderPart headerPart = new HeaderPart(new PartName("/word/heade-" + type.value() + ".xml"));
        Relationship relationship = mainDocumentPart.addTargetPart(headerPart);
        Hdr hdr = null;
        if (waterMark) {
            setWatermarkHdr(headerPart, content);
            hdr = headerPart.getJaxbElement();
        } else {
            hdr = factory.createHdr();
        }
        // Bind the header JAXB elements as representing their header parts
        headerPart.setJaxbElement(hdr);
        P paragraph = factory.createP();
        createHeaderContent(paragraph, true, content);
        hdr.getContent().add(paragraph);

        switch (HdrFtrRef.FIRST) {
            case FIRST:
                sectPr.setTitlePg(new BooleanDefaultTrue());
            case EVEN:
                DocumentSettingsPart documentSettingsPart = mainDocumentPart.getDocumentSettingsPart();
                CTSettings contents = documentSettingsPart.getContents();
                //奇偶不同时设置
                contents.setEvenAndOddHeaders(new BooleanDefaultTrue());
            default:

        }
        // Add the reference to both header parts to the Main Document Part
        HeaderReference headerReference = factory.createHeaderReference();
        headerReference.setType(type);
        headerReference.setId(relationship.getId());
        sectPr.getEGHdrFtrReferences().add(headerReference);

    }

    /**
     * 获取页码信息
     *
     * @return
     */
    public static SdtContentBlock getPageSizeSdt() {
        // 中间内容
        SdtContentBlock sdtContentBlock = factory.createSdtContentBlock();
        sdtContentBlock.getContent().add(getTextField("第"));
        sdtContentBlock.getContent().add(getFieldBegin());
        sdtContentBlock.getContent().add(getPageNumberField());
        sdtContentBlock.getContent().add(getFieldEnd());
        sdtContentBlock.getContent().add(getTextField("页"));
        return sdtContentBlock;
    }

    /**
     * 总页数
     *
     * @return
     */
    public static SdtContentBlock getPageNumberSdt() {
        // 中间内容
        SdtContentBlock sdtContentBlock = factory.createSdtContentBlock();
        sdtContentBlock.getContent().add(getTextField("共"));
        sdtContentBlock.getContent().add(getFieldBegin());
        sdtContentBlock.getContent().add(getTotalPageNumberField());
        sdtContentBlock.getContent().add(getFieldEnd());
        sdtContentBlock.getContent().add(getTextField("页"));
        return sdtContentBlock;
    }

    /**
     * 添加页眉页脚，左中右 三部分内容
     *
     * @return 页脚对象
     */
    private static void createHeaderContent(P paragraph, boolean isHeader, String content) {
        createHeaderFooterContent(paragraph, true, content);
    }

    /**
     * 添加页眉页脚，左中右 三部分内容
     *
     * @return 页脚对象
     */
    private static void createFooterContent(P paragraph, boolean isHeader) {
        createHeaderFooterContent(paragraph, false, null);
    }


    /**
     * 添加页眉页脚，左中右 三部分内容
     *
     * @return 页脚对象
     */
    private static void createHeaderFooterContent(P paragraph, boolean isHeader, String content) {
        RPr fontRPr = getRPr("宋体", "000000", "22", STHint.EAST_ASIA, true, false, false, false);
        R run = factory.createR();
        run.setRPr(fontRPr);
        paragraph.getContent().add(run);

        // tab
        paragraph.getContent().add(getTextField(""));
        R r1 = factory.createR();
        R.Ptab rPtab = factory.createRPtab();
        rPtab.setAlignment(STPTabAlignment.CENTER);
        rPtab.setRelativeTo(STPTabRelativeTo.MARGIN);
        rPtab.setLeader(STPTabLeader.NONE);
        r1.getContent().add(rPtab);
        paragraph.getContent().add(r1);
        //中间内容
        if (isHeader) {
            //页眉
            paragraph.getContent().add(getTextField(content));
        } else {
            //页脚 编码
            if (content == null) {
                SdtContentBlock sdtContentBlock = getPageSizeSdt();
                paragraph.getContent().add(sdtContentBlock);
            } else {
                //页脚
                paragraph.getContent().add(getTextField(content));
            }

        }

        // 右边内容
        R r2 = factory.createR();
        R.Ptab rPtab1 = factory.createRPtab();
        rPtab1.setAlignment(STPTabAlignment.RIGHT);
        rPtab1.setRelativeTo(STPTabRelativeTo.MARGIN);
        rPtab1.setLeader(STPTabLeader.NONE);
        r2.getContent().add(rPtab1);
        // 右边内容
        paragraph.getContent().add(r2);
        if (isHeader) {
            //页眉
            paragraph.getContent().add(getTextField(""));
        } else {
            if (content == null) {
                //页脚
                SdtContentBlock pageNumberSdt = getPageNumberSdt();
                paragraph.getContent().add(pageNumberSdt);
            }
        }

    }


    private static R getTextField(String content) {
        Text text = factory.createText();
        R run = factory.createR();
        text.setValue(content);
        run.getContent().add(text);
        return run;
    }

    private static R getPageNumberField() {
        R run = factory.createR();
        Text txt = new Text();
        txt.setSpace("preserve");
        txt.setValue("PAGE \\* MERGEFORMAT");
        run.getContent().add(factory.createRInstrText(txt));
        return run;
    }

    private static R getTotalPageNumberField() {
        R run = factory.createR();
        Text txt = new Text();
        txt.setSpace("preserve");
        txt.setValue(" NUMPAGES \\* MERGEFORMAT ");
        run.getContent().add(factory.createRInstrText(txt));
        return run;
    }

    private static R getFieldBegin() {
        R run = factory.createR();
        FldChar fldchar = factory.createFldChar();
        fldchar.setFldCharType(STFldCharType.BEGIN);
        run.getContent().add(fldchar);
        return run;
    }

    private static R getFieldEnd() {
        FldChar fldcharend = factory.createFldChar();
        fldcharend.setFldCharType(STFldCharType.END);
        R run = factory.createR();
        run.getContent().add(fldcharend);
        return run;
    }

    /**
     * 获取Rpr对象
     *
     * @param fontFamily
     * @param colorVal
     * @param fontSize
     * @param sTHint
     * @param isBlod
     * @param isUnderLine
     * @param isItalic
     * @param isStrike
     * @return
     */
    public static RPr getRPr(String fontFamily, String colorVal, String fontSize, STHint sTHint, boolean isBlod,
                             boolean isUnderLine, boolean isItalic, boolean isStrike) {
        RPr rPr = factory.createRPr();
        RFonts rf = new RFonts();
        rf.setHint(sTHint);
        rf.setAscii(fontFamily);
        rf.setHAnsi(fontFamily);
        rPr.setRFonts(rf);

        BooleanDefaultTrue bdt = factory.createBooleanDefaultTrue();
        rPr.setBCs(bdt);
        if (isBlod) {
            rPr.setB(bdt);
        }
        if (isItalic) {
            rPr.setI(bdt);
        }
        if (isStrike) {
            rPr.setStrike(bdt);
        }
        if (isUnderLine) {
            U underline = new U();
            underline.setVal(UnderlineEnumeration.SINGLE);
            rPr.setU(underline);
        }

        Color color = new Color();
        color.setVal(colorVal);
        rPr.setColor(color);

        HpsMeasure sz = new HpsMeasure();
        sz.setVal(new BigInteger(fontSize));
        rPr.setSz(sz);
        rPr.setSzCs(sz);
        return rPr;
    }


    /**
     * 水印
     *
     * @param headerPart
     * @param text
     * @throws Exception
     */
    @SneakyThrows
    private static void setWatermarkHdr(HeaderPart headerPart, String text) {

        ImagePngPart imagePart = new ImagePngPart(new PartName("/media/background.png"));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(createWaterMark(text), "png", out);
        byte[] imagebytes = out.toByteArray();
        imagePart.setBinaryData(imagebytes);
        Relationship rel = headerPart.addTargetPart(imagePart, RelationshipsPart.AddPartBehaviour.REUSE_EXISTING);

        String openXML = "<w:hdr mc:Ignorable=\"w14 wp14\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"
                + "<w:p>"
                + "<w:pPr>"
                + "<w:pStyle w:val=\"Header\"/>"
                + "</w:pPr>"
                + "<w:r>"
                + "<w:rPr>"
                + "<w:noProof/>"
                + "</w:rPr>"
                + "<w:pict>"
                + "<v:shapetype coordsize=\"21600,21600\" filled=\"f\" id=\"_x0000_t75\" o:preferrelative=\"t\" o:spt=\"75\" path=\"m@4@5l@4@11@9@11@9@5xe\" stroked=\"f\">"
                + "<v:stroke joinstyle=\"miter\"/>"
                + "<v:formulas>"
                + "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>"
                + "<v:f eqn=\"sum @0 1 0\"/>"
                + "<v:f eqn=\"sum 0 0 @1\"/>"
                + "<v:f eqn=\"prod @2 1 2\"/>"
                + "<v:f eqn=\"prod @3 21600 pixelWidth\"/>"
                + "<v:f eqn=\"prod @3 21600 pixelHeight\"/>"
                + "<v:f eqn=\"sum @0 0 1\"/>"
                + "<v:f eqn=\"prod @6 1 2\"/>"
                + "<v:f eqn=\"prod @7 21600 pixelWidth\"/>"
                + "<v:f eqn=\"sum @8 21600 0\"/>"
                + "<v:f eqn=\"prod @7 21600 pixelHeight\"/>"
                + "<v:f eqn=\"sum @10 21600 0\"/>"
                + "</v:formulas>"
                + "<v:path gradientshapeok=\"t\" o:connecttype=\"rect\" o:extrusionok=\"f\"/>"
                + "<o:lock aspectratio=\"t\" v:ext=\"edit\"/>"
                + "</v:shapetype>"
                + "<v:shape id=\"WordPictureWatermark835936646\" o:allowincell=\"f\" o:spid=\"_x0000_s2050\" style=\"position:absolute;margin-left:0;margin-top:0;width:467.95pt;height:615.75pt;z-index:-251657216;mso-position-horizontal:center;mso-position-horizontal-relative:margin;mso-position-vertical:center;mso-position-vertical-relative:margin\" type=\"#_x0000_t75\">"
                + "<v:imagedata blacklevel=\"22938f\" gain=\"19661f\" o:title=\"docx4j-logo\" r:id=\"" + rel.getId() + "\"/>"
                + "</v:shape>"
                + "</w:pict>"
                + "</w:r>"
                + "</w:p>"
                + "</w:hdr>";

        Hdr hdr = (Hdr) XmlUtils.unmarshalString(openXML);
        headerPart.setJaxbElement(hdr);
    }

    /**
     * 水印
     *
     * @param content
     * @return
     */
    private static BufferedImage createWaterMark(String content) {
        Integer width = 1000;
        Integer height = 1360;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
        String fontType = "宋体";
        Integer fontStyle = Font.PLAIN;
        Integer fontSize = 30;
        Font font = new Font(fontType, fontStyle, fontSize);
        Graphics2D g2d = image.createGraphics(); // 获取Graphics2d对象
        image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        g2d.dispose();
        for (int i = 1; i <= 20; i += 2) {
            for (int j = 1; j <= 10; j += 2) {
                int px = j * 100;
                int py = i * 100;
                g2d = image.createGraphics();
                g2d.setColor(java.awt.Color.black);
                g2d.setStroke(new BasicStroke(1)); // 设置字体
                g2d.setFont(font); // 设置字体类型 加粗 大小
                g2d.translate(px, py);// 设置原点
                g2d.rotate(Math.toRadians(-30));// 设置倾斜度
                FontRenderContext context = g2d.getFontRenderContext();
                Rectangle2D bounds = font.getStringBounds(content, context);
                g2d.drawString(content, 0, 0);
                g2d.dispose();
            }
        }
        return image;
    }

    private static P makePageBr() throws Exception {
        P p = factory.createP();
        R r = factory.createR();
        Br br = factory.createBr();
        br.setType(STBrType.PAGE);
        r.getContent().add(br);
        p.getContent().add(r);
        return p;
    }


    // create a header
    public static Hdr createHeader(String content) {
        ObjectFactory factory = Context.getWmlObjectFactory();
        Hdr header = factory.createHdr();
        P paragraph = factory.createP();
        PPr ppr = factory.createPPr();
        Jc jc = factory.createJc();
        jc.setVal(JcEnumeration.CENTER);
        ppr.setJc(jc);
        paragraph.setPPr(ppr);
        R run = factory.createR();
        Text text = new Text();
        text.setValue(content);
        run.getContent().add(text);
        paragraph.getContent().add(run);
        header.getContent().add(paragraph);
        return header;
    }

    // create a header with page number
    private static Ftr createFooterWithPageNumber() {
        ObjectFactory factory = Context.getWmlObjectFactory();
        Ftr ftr = factory.createFtr();
        P paragraph = factory.createP();
        PPr ppr = factory.createPPr();
        Jc jc = factory.createJc();
        jc.setVal(JcEnumeration.RIGHT);
        ppr.setJc(jc);
        paragraph.setPPr(ppr);
        addFieldBegin(paragraph);
        addPageNumberField(paragraph);
        addFieldEnd(paragraph);
        ftr.getContent().add(paragraph);
        return ftr;
    }

    private static void addPageNumberField(P paragraph) {
        ObjectFactory factory = Context.getWmlObjectFactory();
        Text txt = new Text();
        txt.setSpace("preserve");
        txt.setValue(" PAGE   \\* ArabicDash ");
        R run = factory.createR();
        run.getContent().add(factory.createRInstrText(txt));
        paragraph.getContent().add(run);
    }

    private static void addFieldBegin(P paragraph) {
        ObjectFactory factory = Context.getWmlObjectFactory();
        FldChar fldChar = factory.createFldChar();
        fldChar.setFldCharType(STFldCharType.BEGIN);
        R run = factory.createR();
        run.getContent().add(fldChar);
        paragraph.getContent().add(run);
    }

    private static void addFieldEnd(P paragraph) {
        ObjectFactory factory = Context.getWmlObjectFactory();
        FldChar fldChar = factory.createFldChar();
        fldChar.setFldCharType(STFldCharType.END);
        R run = factory.createR();
        run.getContent().add(fldChar);
        paragraph.getContent().add(run);
    }

    /**
     * 创建页脚的组件 添加页脚并添加页码
     *
     * @param mlPackage
     * @return
     */
    @SneakyThrows
    public static Relationship createFooterPart(WordprocessingMLPackage mlPackage) {
        FooterPart footerPart = new FooterPart();
        footerPart.setPackage(mlPackage);
        footerPart.setJaxbElement(createFooterWithPageNr());
        return mlPackage.getMainDocumentPart().addTargetPart(footerPart);
    }

    /**
     * 创建页脚引用关系
     *
     * @param wordMLPackage
     * @param relationship
     */
    public static void createFooter(WordprocessingMLPackage wordMLPackage, Relationship relationship) {
        List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();
        ObjectFactory factory = Context.getWmlObjectFactory();
        SectPr sectionProperties = sections.get(sections.size() - 1).getSectPr();
        // There is always a section wrapper, but it might not contain a sectPr
        if (sectionProperties == null) {
            sectionProperties = factory.createSectPr();
            wordMLPackage.getMainDocumentPart().addObject(sectionProperties);
            sections.get(sections.size() - 1).setSectPr(sectionProperties);
        }

        FooterReference footerReference = factory.createFooterReference();
        footerReference.setId(relationship.getId());
        footerReference.setType(HdrFtrRef.DEFAULT);
        sectionProperties.getEGHdrFtrReferences().add(footerReference);
    }


    /**
     * 生成页码
     *
     * @return
     */
    public static Ftr createFooterWithPageNr() {
        ObjectFactory factory = Context.getWmlObjectFactory();
        Ftr ftr = factory.createFtr();
        P paragraph = factory.createP();

        addFieldBegin(paragraph);
        addPageNumberField(paragraph);
        addFieldEnd(paragraph);

        ftr.getContent().add(paragraph);
        return ftr;
    }

    public static SectPr getSectPr(WordprocessingMLPackage wordMLPackage) {
        List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();
        SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
        if (sectPr == null) {
            sectPr = factory.createSectPr();
            wordMLPackage.getMainDocumentPart().addObject(sectPr);
            sections.get(sections.size() - 1).setSectPr(sectPr);
        }
        return sectPr;
    }
}